#!usr/bin/python
# -*- coding: utf8 -*-
encoding='utf8'

import os, sys, re, codecs
from Source import Source, readFile
from Uncomment import Uncomment
from Undefine import Defines, Define

## Start concatenation ##

'''
Process following commands:
---------- Done ----------
    #include
    #define
    #undef
    #ifdef
    #ifndef
    #endif
    #else
--------- Undone ---------
    #if
    #elif
Processing is divided on steps:
1) work out all include directives
1) work out all define directives
'''

class Preprocess(object):
    def __init__(self, source):
        self.source = source
        #Regexps:
        self.re_global_include = re.compile(u'(?iu)^\s*#include\s+\<(\S+?)\>')
        self.re_local_include  = re.compile(u'(?iu)^\s*#include\s+\"(\S+?)\"')
        self.re_undef   = re.compile(u'(?iu)^\s*#undef\s+(\S+)')
        self.re_ifdef   = re.compile(u'(?iu)^\s*#ifdef\s+(\S+)')
        self.re_ifndef  = re.compile(u'(?iu)^\s*#ifndef\s+(\S+)')
        self.re_endif   = re.compile(u'(?iu)^\s*#endif')
        self.re_define  = re.compile(u'(?iu)^\s*#define')
        self.re_else    = re.compile(u'(?iu)^\s*#else')
        #Counter:
        self.ifdef_erase = 0
        self.include_paths = []
        #Define
        self.DEFINES = Defines()
        cParse_flag = Define()
        cParse_flag.match(u'#define __cParser')
        self.DEFINES.append(cParse_flag)

    def preprocess(self, apply_defines = True):
        self.prepare(self.source)
        u = Uncomment(self.source)
        u.replaceWith(u'')
        for line in self.source.byLine():
            if self.checkDefinitions(line):
                continue
            m = self.re_global_include.match(line)
            if m:
                # Deny global includes (for that shit with stdio.h)
                # self.include(m.group(1))
                self.source.removeLine()
                continue
            m = self.re_local_include.match(line)
            if m:
                self.include(m.group(1))
                continue
            if apply_defines:
                self.source.replaceLine(self.DEFINES.apply(line))

    def addInclude_path(self, path):
        self.include_paths.append(path)

    def checkDefinitions(self, line):
        # Check all regexps
        _ifdef  = self.re_ifdef.match(line)
        _ifndef = self.re_ifndef.match(line)
        _else   = self.re_else.match(line)
        _endif  = self.re_endif.match(line)
        _define = self.re_define.match(line)
        _undef  = self.re_undef.match(line)
        line_removed = False

        # Remove line if occured
        for i, d in enumerate([ _ifdef, _ifndef, _else, _endif, _define, _undef, self.ifdef_erase ]):
            if d not in [False, None, 0]:
                #self.source.replaceLine(u'//' + line)
                self.source.removeLine(True)
                line_removed = True
                break
        else:
            return line_removed

        # Main work
        if self.ifdef_erase > 0:
            if _endif or _else:
                self.ifdef_erase-= 1
        elif _endif:
            pass # Wow
        elif _else:
            self.ifdef_erase+= 1
        elif _define:
            define = Define()
            if define.match(line):
                self.DEFINES.append(define)
        elif _undef:
            self.DEFINES.remove(_undef.group(1))
        elif _ifdef:
            if not self.DEFINES.exists(_ifdef.group(1)):
                self.ifdef_erase+= 1
        elif _ifndef:
            if self.DEFINES.exists(_ifndef.group(1)):
                self.ifdef_erase+= 1
        return line_removed

    def include(self, filepath):
        if not os.path.exists(filepath):
            for incpath in self.include_paths:
                tmp_path = os.path.join(incpath, filepath)
                if os.path.exists(tmp_path):
                    filepath = tmp_path
                    break
            else:
                print u'Error: Preprocess.include(): File not found:'
                print u'%s' % (self.source.origin())
                print u'Absolute pass:', os.path.abspath(filepath)
                raise ValueError("File not found")
        try:
            content = readFile(filepath)
        except UnicodeDecodeError, err:
            print 'UnicodeDecodeError in', os.path.abspath(filepath)
            raise err
        source = Source(content)
        source.setFilename(filepath)
        self.prepare(source)
        self.source.removeLine()
        self.source.include(source)
        self.source.moveLine(-1)

    def prepare(self, source):
        self.concatLines(source)
        u = Uncomment(source)
        u.replaceWith(u'')

    def concatLines(self, source):
        maybe = False
        result = u''
        for line in source.byLine():
            i = len(line) - 1
            count = 0
            while (i >= 0) and (line[i] == u'\\'):
                i-= 1
                count+= 1
            if count % 2 == 1:
                source.removeLine()
                next = source.current()
                source.replaceLine(line[:-1] + next)
                source.moveLine(-1)

## Stop concatenation ##

def test1():
    code = readFile('tests/undefine2.cpp')
    s = Source(code)
    p = Preprocess(s)
    p.preprocess()
    print '='*20
    print '     Result:'
    print '='*20
    print s.get()

def main():
    test1()

if __name__ == '__main__':
    main()
